//
// "$Id: fl_set_fonts_x.cxx 8864 2011-07-19 04:49:30Z greg.ercolano $"
//
// X11 font utilities for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2010 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file.  If this
// file is missing or damaged, see the license at:
//
//     http://www.fltk.org/COPYING.php
//
// Please report all bugs and problems on the following page:
//
//     http://www.fltk.org/str.php
//

// This function fills in the fltk font table with all the fonts that
// are found on the X server.  It tries to place the fonts into families
// and to sort them so the first 4 in a family are normal, bold, italic,
// and bold italic.

// Standard X fonts are matched by a pattern that is always of
// this form, and this pattern is put in the table:
// "-*-family-weight-slant-width1-style-*-registry-encoding"

// Non-standard font names (those not starting with '-') are matched
// by a pattern of the form "prefix*suffix", where the '*' is where
// fltk thinks the point size is, or by the actual font name if no
// point size is found.

// Fltk knows how to pull an "attribute" out of a font name, such as
// bold or italic, by matching known x font field values.  All words
// that don't match a known attribute are combined into the "name"
// of the font.  Names are compared before attributes for sorting, this
// makes the bold and plain version of a font come out next to each
// other despite the poor X font naming scheme.

// By default fl_set_fonts() only does iso8859-1 encoded fonts.  You can
// do all normal X fonts by passing "-*" or every possible font with "*".

// Fl::set_font will take strings other than the ones this stores
// and can identify any font on X that way.  You may want to write your
// own system of font management and not use this code.

// turn word N of a X font name into either some attribute bits
// (right now 0, FL_BOLD, or FL_ITALIC), or into -1 indicating that
// the word should be put into the name:

#if __FLTK_LINUX__

static int attribute(int n, const char *p)
{
	// don't put blank things into name:
	if (!*p || *p=='-' || *p=='*') return 0;
	if (n == 3) { // weight
		if (!strncmp(p,"normal",6) ||
		    !strncmp(p,"light",5) ||
		    !strncmp(p,"medium",6) ||
		    !strncmp(p,"book",4)) return 0;
		if (!strncmp(p,"bold",4) || !strncmp(p,"demi",4)) return FL_BOLD;
	} else if (n == 4) { // slant
		if (*p == 'r') return 0;
		if (*p == 'i' || *p == 'o') return FL_ITALIC;
	} else if (n == 5) { // sWidth
		if (!strncmp(p,"normal",6)) return 0;
	}
	return -1;
}

// return non-zero if the registry-encoding should be used:
extern const char* fl_encoding;
static int use_registry(const char *p)
{
	return *p && *p!='*' && strcmp(p,fl_encoding);
}

// Bug: older versions calculated the value for *ap as a side effect of
// making the name, and then forgot about it. To avoid having to change
// the header files I decided to store this value in the last character
// of the font name array.
#define ENDOFBUFFER 127 // sizeof(Fl_Font.fontname)-1

// turn a stored (with *'s) X font name into a pretty name:
const char* Fl::get_font_name(Fl_Font fnum, int* ap)
{
	Fl_Fontdesc *f = fl_fonts + fnum;
	if (!f->fontname[0]) {
		int type = 0;
		const char* p = f->name;
		if (!p) {
			if (ap) *ap = 0;
			return "";
		}
		char *o = f->fontname;

		if (*p != '-') { // non-standard font, just replace * with spaces:
			if (strstr(p,"bold")) type = FL_BOLD;
			if (strstr(p,"ital")) type |= FL_ITALIC;
			for (; *p; p++) {
				if (*p == '*' || *p == ' ' || *p == '-') {
					do p++;
					while (*p == '*' || *p == ' ' || *p == '-');
					if (!*p) break;
					if (o < (f->fontname + ENDOFBUFFER - 1)) *o++ = ' ';
				}
				if (o < (f->fontname + ENDOFBUFFER - 1)) *o++ = *p;
			}
			*o = 0;

		} else { // standard dash-separated font:

			// get the family:
			const char *x = fl_font_word(p,2);
			if (*x) x++;
			if (*x=='*') x++;
			if (!*x) {
				if (ap) *ap = 0;
				return p;
			}
			const char *e = fl_font_word(x,1);
			if ((e - x) < (int)(ENDOFBUFFER - 1)) {
				// MRS: we want strncpy here, not strlcpy...
				strncpy(o,x,e-x);
				o += e-x;
			} else {
				strlcpy(f->fontname, x, ENDOFBUFFER);
				o = f->fontname+ENDOFBUFFER-1;
			}

			// collect all the attribute words:
			for (int n = 3; n <= 6; n++) {
				// get the next word:
				if (*e) e++;
				x = e;
				e = fl_font_word(x,1);
				int t = attribute(n,x);
				if (t < 0) {
					if (o < (f->fontname + ENDOFBUFFER - 1)) *o++ = ' ';
					if ((e - x) < (int)(ENDOFBUFFER - (o - f->fontname) - 1)) {
						// MRS: we want strncpy here, not strlcpy...
						strncpy(o,x,e-x);
						o += e-x;
					} else {
						strlcpy(o,x, ENDOFBUFFER - (o - f->fontname) - 1);
						o = f->fontname+ENDOFBUFFER-1;
					}
				} else type |= t;
			}

			// skip over the '*' for the size and get the registry-encoding:
			x = fl_font_word(e,2);
			if (*x) {
				x++;
				*o++ = '(';
				while (*x) *o++ = *x++;
				*o++ = ')';
			}

			*o = 0;
			if (type & FL_BOLD) strlcat(f->fontname, " bold", ENDOFBUFFER);
			if (type & FL_ITALIC) strlcat(f->fontname, " italic", ENDOFBUFFER);
		}
		f->fontname[ENDOFBUFFER] = (char)type;
	}
	if (ap) *ap = f->fontname[ENDOFBUFFER];
	return f->fontname;
}

extern "C" {
// sort raw (non-'*') X font names into perfect order:

	static int ultrasort(const void *aa, const void *bb)
	{
		const char *a = *(char **)aa;
		const char *b = *(char **)bb;

		// sort all non x-fonts at the end:
		if (*a != '-') {
			if (*b == '-') return 1;
			// 2 non-x fonts are matched by "numeric sort"
			int ret = 0;
			for (;;) {
				if (isdigit(*a) && isdigit(*b)) {
					int na = strtol(a, (char **)&a, 10);
					int nb = strtol(b, (char **)&b, 10);
					if (!ret) ret = na-nb;
				} else if (*a != *b) {
					return (*a-*b);
				} else if (!*a) {
					return ret;
				} else {
					a++;
					b++;
				}
			}
		} else {
			if (*b != '-') return -1;
		}

		// skip the foundry (assume equal):
		for (a++; *a && *a++!='-';);
		for (b++; *b && *b++!='-';);

		// compare the family and all the attribute words:
		int atype = 0;
		int btype = 0;
		for (int n = 2; n <= 6; n++) {
			int at = attribute(n,a);
			int bt = attribute(n,b);
			if (at < 0) {
				if (bt >= 0) return 1;
				for (;;) {
					if (*a!=*b) return *a-*b;
					b++;
					if (!*a || *a++=='-') break;
				}
			} else {
				if (bt < 0) return -1;
				a = fl_font_word(a,1);
				if (*a) a++;
				b = fl_font_word(b,1);
				if (*b) b++;
				atype |= at;
				btype |= bt;
			}
		}

		// remember the pixel size:
		int asize = atoi(a);
		int bsize = atoi(b);

		// compare the registry/encoding:
		a = fl_font_word(a,6);
		if (*a) a++;
		b = fl_font_word(b,6);
		if (*b) b++;
		if (use_registry(a)) {
			if (!use_registry(b)) return 1;
			int r = strcmp(a,b);
			if (r) return r;
		} else {
			if (use_registry(b)) return -1;
		}

		if (atype != btype) return atype-btype;
		if (asize != bsize) return asize-bsize;

		// something wrong, just do a string compare...
		return strcmp(*(char**)aa, *(char**)bb);
	}
}

// converts a X font name to a standard starname, returns point size:
static int to_canonical(char *to, const char *from, size_t tolen)
{
	char* c = fl_find_fontsize((char*)from);
	if (!c) return -1; // no point size found...
	const char* endptr;
	int size = strtol(c,(char**)&endptr,10);
	if (from[0] == '-') {
		// replace the "foundry" with -*-:
		*to++ = '-';
		*to++ = '*';
		for (from++; *from && *from != '-'; from++);
		// skip to the registry-encoding:
		endptr = (char*)fl_font_word(endptr,6);
		if (*endptr && !use_registry(endptr+1)) endptr = "";
	}
	int n = c-from;
	// MRS: we want strncpy here, not strlcpy...
	if (n > (int)(tolen - 1)) return -1;
	strncpy(to,from,n);
	to[n++] = '*';
	strlcpy(to+n,endptr, tolen - n);
	return size;
}

static unsigned int fl_free_font = FL_FREE_FONT;

Fl_Font Fl::set_fonts(const char* xstarname)
{
	if (fl_free_font > (unsigned)FL_FREE_FONT) // already been here
		return (Fl_Font)fl_free_font;
	fl_open_display();
	int xlistsize;
	char buf[20];
	if (!xstarname) {
		strcpy(buf,"-*-");
		strcpy(buf+3,fl_encoding);
		xstarname = buf;
	}
	char **xlist = XListFonts(fl_display, xstarname, 10000, &xlistsize);
	if (!xlist) return (Fl_Font)fl_free_font;
	qsort(xlist, xlistsize, sizeof(*xlist), ultrasort);
	int used_xlist = 0;
	for (int i=0; i<xlistsize;) {
		int first_xlist = i;
		const char *p = xlist[i++];
		char canon[1024];
		int size = to_canonical(canon, p, sizeof(canon));
		if (size >= 0) {
			for (;;) { // find all matching fonts:
				if (i >= xlistsize) break;
				const char *q = xlist[i];
				char this_canon[1024];
				if (to_canonical(this_canon, q, sizeof(this_canon)) < 0) break;
				if (strcmp(canon, this_canon)) break;
				i++;
			}
			/*if (*p=='-' || i > first_xlist+1)*/ p = canon;
		}
		unsigned int j;
		for (j = 0;; j++) {
			/*if (j < FL_FREE_FONT) {
			// see if it is one of our built-in fonts:
			// if so, set the list of x fonts, since we have it anyway
			if (fl_fonts[j].name && !strcmp(fl_fonts[j].name, p)) break;
			} else */{
				j = fl_free_font++;
				if (p == canon) p = strdup(p);
				else used_xlist = 1;
				Fl::set_font((Fl_Font)j, p);
				break;
			}
		}
		if (!fl_fonts[j].xlist) {
			fl_fonts[j].xlist = xlist+first_xlist;
			fl_fonts[j].n = -(i-first_xlist);
			used_xlist = 1;
		}
	}
	if (!used_xlist) XFreeFontNames(xlist);
	return (Fl_Font)fl_free_font;
}

int Fl::get_font_sizes(Fl_Font fnum, int*& sizep)
{
	Fl_Fontdesc *s = fl_fonts+fnum;
	if (!s->name) s = fl_fonts; // empty slot in table, use entry 0
	if (!s->xlist) {
		fl_open_display();
		s->xlist = XListFonts(fl_display, s->name, 100, &(s->n));
		if (!s->xlist) return 0;
	}
	int listsize = s->n;
	if (listsize<0) listsize = -listsize;
	static int sizes[128];
	int numsizes = 0;
	for (int i = 0; i < listsize; i++) {
		char *q = s->xlist[i];
		char *d = fl_find_fontsize(q);
		if (!d) continue;
		int s = strtol(d,0,10);
		if (!numsizes || sizes[numsizes-1] < s) {
			sizes[numsizes++] = s;
		} else {
			// insert-sort the new size into list:
			int n;
			for (n = numsizes-1; n > 0; n--) if (sizes[n-1] < s) break;
			if (sizes[n] != s) {
				for (int m = numsizes; m > n; m--) sizes[m] = sizes[m-1];
				sizes[n] = s;
				numsizes++;
			}
		}
	}
	sizep = sizes;
	return numsizes;
}

#endif

//
// End of "$Id: fl_set_fonts_x.cxx 8864 2011-07-19 04:49:30Z greg.ercolano $".
//
